+++ /dev/null
-From 4658f57ba7f60c3bd8e14c1ca7acf2090aee8436 Mon Sep 17 00:00:00 2001
-From: Mikhail Kshevetskiy <mikhail.kshevetskiy@iopsys.eu>
-Date: Tue, 12 Aug 2025 06:21:35 +0300
-Subject: [PATCH v6 02/13] spi: airoha: remove unnecessary restriction length
-
-The "length < 160" restriction is not needed because airoha_snand_write_data()
-and airoha_snand_read_data() will properly handle data transfers above
-SPI_MAX_TRANSFER_SIZE.
-
-Signed-off-by: Mikhail Kshevetskiy <mikhail.kshevetskiy@iopsys.eu>
-Reviewed-by: AngeloGioacchino Del Regno <angelogioacchino.delregno@collabora.com>
----
- drivers/spi/spi-airoha-snfi.c | 7 -------
- 1 file changed, 7 deletions(-)
-
---- a/drivers/spi/spi-airoha-snfi.c
-+++ b/drivers/spi/spi-airoha-snfi.c
-@@ -619,13 +619,6 @@ static int airoha_snand_adjust_op_size(s
-
- if (op->data.nbytes > max_len)
- op->data.nbytes = max_len;
-- } else {
-- max_len = 1 + op->addr.nbytes + op->dummy.nbytes;
-- if (max_len >= 160)
-- return -EOPNOTSUPP;
--
-- if (op->data.nbytes > 160 - max_len)
-- op->data.nbytes = 160 - max_len;
- }
-
- return 0;
+++ /dev/null
-From fb41a3e3bc357592b28a8abb504df99dad642588 Mon Sep 17 00:00:00 2001
-From: Mikhail Kshevetskiy <mikhail.kshevetskiy@iopsys.eu>
-Date: Mon, 11 Aug 2025 13:09:51 +0300
-Subject: [PATCH v6 04/13] spi: airoha: remove unnecessary switch to non-dma
- mode
-
-The code switches to dma at the start of dirmap operation and returns
-to non-dma at the end of dirmap operation, so an additional switch to
-non-dma at the start of dirmap write is not required.
-
-Signed-off-by: Mikhail Kshevetskiy <mikhail.kshevetskiy@iopsys.eu>
-Acked-by: Lorenzo Bianconi <lorenzo@kernel.org>
-Reviewed-by: AngeloGioacchino Del Regno <angelogioacchino.delregno@collabora.com>
----
- drivers/spi/spi-airoha-snfi.c | 3 ---
- 1 file changed, 3 deletions(-)
-
---- a/drivers/spi/spi-airoha-snfi.c
-+++ b/drivers/spi/spi-airoha-snfi.c
-@@ -815,9 +815,6 @@ static ssize_t airoha_snand_dirmap_write
- int err;
-
- as_ctrl = spi_controller_get_devdata(spi->controller);
-- err = airoha_snand_set_mode(as_ctrl, SPI_MODE_MANUAL);
-- if (err < 0)
-- return err;
-
- memcpy(txrx_buf + offs, buf, len);
- dma_addr = dma_map_single(as_ctrl->dev, txrx_buf, SPI_NAND_CACHE_SIZE,
--- /dev/null
+From 661856ca131c8bf6724905966e02149805660abe Mon Sep 17 00:00:00 2001
+From: Mikhail Kshevetskiy <mikhail.kshevetskiy@iopsys.eu>
+Date: Sun, 12 Oct 2025 15:16:53 +0300
+Subject: [PATCH 05/14] spi: airoha: remove unnecessary restriction length
+
+The "length < 160" restriction is not needed because airoha_snand_write_data()
+and airoha_snand_read_data() will properly handle data transfers above
+SPI_MAX_TRANSFER_SIZE.
+
+Signed-off-by: Mikhail Kshevetskiy <mikhail.kshevetskiy@iopsys.eu>
+Reviewed-by: AngeloGioacchino Del Regno <angelogioacchino.delregno@collabora.com>
+Link: https://patch.msgid.link/20251012121707.2296160-3-mikhail.kshevetskiy@iopsys.eu
+Signed-off-by: Mark Brown <broonie@kernel.org>
+---
+ drivers/spi/spi-airoha-snfi.c | 7 -------
+ 1 file changed, 7 deletions(-)
+
+--- a/drivers/spi/spi-airoha-snfi.c
++++ b/drivers/spi/spi-airoha-snfi.c
+@@ -619,13 +619,6 @@ static int airoha_snand_adjust_op_size(s
+
+ if (op->data.nbytes > max_len)
+ op->data.nbytes = max_len;
+- } else {
+- max_len = 1 + op->addr.nbytes + op->dummy.nbytes;
+- if (max_len >= 160)
+- return -EOPNOTSUPP;
+-
+- if (op->data.nbytes > 160 - max_len)
+- op->data.nbytes = 160 - max_len;
+ }
+
+ return 0;
--- /dev/null
+From 7350f8dc15bfbb7abf1ce4babea6fcace1c574c5 Mon Sep 17 00:00:00 2001
+From: Mikhail Kshevetskiy <mikhail.kshevetskiy@iopsys.eu>
+Date: Sun, 12 Oct 2025 15:16:55 +0300
+Subject: [PATCH 06/14] spi: airoha: remove unnecessary switch to non-dma mode
+
+The code switches to dma at the start of dirmap operation and returns
+to non-dma at the end of dirmap operation, so an additional switch to
+non-dma at the start of dirmap write is not required.
+
+Signed-off-by: Mikhail Kshevetskiy <mikhail.kshevetskiy@iopsys.eu>
+Acked-by: Lorenzo Bianconi <lorenzo@kernel.org>
+Reviewed-by: AngeloGioacchino Del Regno <angelogioacchino.delregno@collabora.com>
+Link: https://patch.msgid.link/20251012121707.2296160-5-mikhail.kshevetskiy@iopsys.eu
+Signed-off-by: Mark Brown <broonie@kernel.org>
+---
+ drivers/spi/spi-airoha-snfi.c | 3 ---
+ 1 file changed, 3 deletions(-)
+
+--- a/drivers/spi/spi-airoha-snfi.c
++++ b/drivers/spi/spi-airoha-snfi.c
+@@ -815,9 +815,6 @@ static ssize_t airoha_snand_dirmap_write
+ int err;
+
+ as_ctrl = spi_controller_get_devdata(spi->controller);
+- err = airoha_snand_set_mode(as_ctrl, SPI_MODE_MANUAL);
+- if (err < 0)
+- return err;
+
+ memcpy(txrx_buf + offs, buf, len);
+ dma_addr = dma_map_single(as_ctrl->dev, txrx_buf, SPI_NAND_CACHE_SIZE,
+++ /dev/null
-From 995b1a65206ee28d5403db0518cb230f2ce429ef Mon Sep 17 00:00:00 2001
-From: Mikhail Kshevetskiy <mikhail.kshevetskiy@iopsys.eu>
-Date: Mon, 11 Aug 2025 19:57:43 +0300
-Subject: [PATCH v6 07/13] spi: airoha: unify dirmap read/write code
-
-Makes dirmap writing looks similar to dirmap reading. Just a minor
-refactoring, no behavior change is expected.
-
-Signed-off-by: Mikhail Kshevetskiy <mikhail.kshevetskiy@iopsys.eu>
----
- drivers/spi/spi-airoha-snfi.c | 50 ++++++++++++++++++++++-------------
- 1 file changed, 32 insertions(+), 18 deletions(-)
-
---- a/drivers/spi/spi-airoha-snfi.c
-+++ b/drivers/spi/spi-airoha-snfi.c
-@@ -672,6 +672,8 @@ static ssize_t airoha_snand_dirmap_read(
- u32 val, rd_mode;
- int err;
-
-+ as_ctrl = spi_controller_get_devdata(spi->controller);
-+
- switch (op->cmd.opcode) {
- case SPI_NAND_OP_READ_FROM_CACHE_DUAL:
- rd_mode = 1;
-@@ -684,7 +686,6 @@ static ssize_t airoha_snand_dirmap_read(
- break;
- }
-
-- as_ctrl = spi_controller_get_devdata(spi->controller);
- err = airoha_snand_set_mode(as_ctrl, SPI_MODE_DMA);
- if (err < 0)
- return err;
-@@ -748,7 +749,7 @@ static ssize_t airoha_snand_dirmap_read(
- if (err)
- goto error_dma_unmap;
-
-- /* trigger dma start read */
-+ /* trigger dma reading */
- err = regmap_clear_bits(as_ctrl->regmap_nfi, REG_SPI_NFI_CON,
- SPI_NFI_RD_TRIG);
- if (err)
-@@ -806,37 +807,47 @@ error_dma_mode_off:
- static ssize_t airoha_snand_dirmap_write(struct spi_mem_dirmap_desc *desc,
- u64 offs, size_t len, const void *buf)
- {
-- struct spi_mem_op *op = &desc->info.op_tmpl;
- struct spi_device *spi = desc->mem->spi;
- u8 *txrx_buf = spi_get_ctldata(spi);
- struct airoha_snand_ctrl *as_ctrl;
- dma_addr_t dma_addr;
-- u32 wr_mode, val;
-+ u32 wr_mode, val, opcode;
- int err;
-
- as_ctrl = spi_controller_get_devdata(spi->controller);
-
-+ opcode = desc->info.op_tmpl.cmd.opcode;
-+ switch (opcode) {
-+ case SPI_NAND_OP_PROGRAM_LOAD_SINGLE:
-+ case SPI_NAND_OP_PROGRAM_LOAD_RAMDOM_SINGLE:
-+ wr_mode = 0;
-+ break;
-+ case SPI_NAND_OP_PROGRAM_LOAD_QUAD:
-+ case SPI_NAND_OP_PROGRAM_LOAD_RAMDON_QUAD:
-+ wr_mode = 2;
-+ break;
-+ default:
-+ /* unknown opcode */
-+ return -EOPNOTSUPP;
-+ }
-+
- memcpy(txrx_buf + offs, buf, len);
-- dma_addr = dma_map_single(as_ctrl->dev, txrx_buf, SPI_NAND_CACHE_SIZE,
-- DMA_TO_DEVICE);
-- err = dma_mapping_error(as_ctrl->dev, dma_addr);
-- if (err)
-- return err;
-
- err = airoha_snand_set_mode(as_ctrl, SPI_MODE_DMA);
- if (err < 0)
-- goto error_dma_unmap;
-+ return err;
-
- err = airoha_snand_nfi_config(as_ctrl);
- if (err)
-- goto error_dma_unmap;
-+ goto error_dma_mode_off;
-
-- if (op->cmd.opcode == SPI_NAND_OP_PROGRAM_LOAD_QUAD ||
-- op->cmd.opcode == SPI_NAND_OP_PROGRAM_LOAD_RAMDON_QUAD)
-- wr_mode = BIT(1);
-- else
-- wr_mode = 0;
-+ dma_addr = dma_map_single(as_ctrl->dev, txrx_buf, SPI_NAND_CACHE_SIZE,
-+ DMA_TO_DEVICE);
-+ err = dma_mapping_error(as_ctrl->dev, dma_addr);
-+ if (err)
-+ goto error_dma_mode_off;
-
-+ /* set dma addr */
- err = regmap_write(as_ctrl->regmap_nfi, REG_SPI_NFI_STRADDR,
- dma_addr);
- if (err)
-@@ -850,12 +861,13 @@ static ssize_t airoha_snand_dirmap_write
- if (err)
- goto error_dma_unmap;
-
-+ /* set write command */
- err = regmap_write(as_ctrl->regmap_nfi, REG_SPI_NFI_PG_CTL1,
-- FIELD_PREP(SPI_NFI_PG_LOAD_CMD,
-- op->cmd.opcode));
-+ FIELD_PREP(SPI_NFI_PG_LOAD_CMD, opcode));
- if (err)
- goto error_dma_unmap;
-
-+ /* set write mode */
- err = regmap_write(as_ctrl->regmap_nfi, REG_SPI_NFI_SNF_MISC_CTL,
- FIELD_PREP(SPI_NFI_DATA_READ_WR_MODE, wr_mode));
- if (err)
-@@ -887,6 +899,7 @@ static ssize_t airoha_snand_dirmap_write
- if (err)
- goto error_dma_unmap;
-
-+ /* trigger dma writing */
- err = regmap_clear_bits(as_ctrl->regmap_nfi, REG_SPI_NFI_CON,
- SPI_NFI_WR_TRIG);
- if (err)
-@@ -931,6 +944,7 @@ static ssize_t airoha_snand_dirmap_write
- error_dma_unmap:
- dma_unmap_single(as_ctrl->dev, dma_addr, SPI_NAND_CACHE_SIZE,
- DMA_TO_DEVICE);
-+error_dma_mode_off:
- airoha_snand_set_mode(as_ctrl, SPI_MODE_MANUAL);
- return err;
- }
--- /dev/null
+From 233a22687411ea053a4b169c07324ee6aa33bf38 Mon Sep 17 00:00:00 2001
+From: Mikhail Kshevetskiy <mikhail.kshevetskiy@iopsys.eu>
+Date: Sun, 12 Oct 2025 15:16:58 +0300
+Subject: [PATCH 07/14] spi: airoha: unify dirmap read/write code
+
+Makes dirmap writing looks similar to dirmap reading. Just a minor
+refactoring, no behavior change is expected.
+
+Signed-off-by: Mikhail Kshevetskiy <mikhail.kshevetskiy@iopsys.eu>
+Link: https://patch.msgid.link/20251012121707.2296160-8-mikhail.kshevetskiy@iopsys.eu
+Signed-off-by: Mark Brown <broonie@kernel.org>
+---
+ drivers/spi/spi-airoha-snfi.c | 50 ++++++++++++++++++++++-------------
+ 1 file changed, 32 insertions(+), 18 deletions(-)
+
+--- a/drivers/spi/spi-airoha-snfi.c
++++ b/drivers/spi/spi-airoha-snfi.c
+@@ -672,6 +672,8 @@ static ssize_t airoha_snand_dirmap_read(
+ u32 val, rd_mode;
+ int err;
+
++ as_ctrl = spi_controller_get_devdata(spi->controller);
++
+ switch (op->cmd.opcode) {
+ case SPI_NAND_OP_READ_FROM_CACHE_DUAL:
+ rd_mode = 1;
+@@ -684,7 +686,6 @@ static ssize_t airoha_snand_dirmap_read(
+ break;
+ }
+
+- as_ctrl = spi_controller_get_devdata(spi->controller);
+ err = airoha_snand_set_mode(as_ctrl, SPI_MODE_DMA);
+ if (err < 0)
+ return err;
+@@ -748,7 +749,7 @@ static ssize_t airoha_snand_dirmap_read(
+ if (err)
+ goto error_dma_unmap;
+
+- /* trigger dma start read */
++ /* trigger dma reading */
+ err = regmap_clear_bits(as_ctrl->regmap_nfi, REG_SPI_NFI_CON,
+ SPI_NFI_RD_TRIG);
+ if (err)
+@@ -806,37 +807,47 @@ error_dma_mode_off:
+ static ssize_t airoha_snand_dirmap_write(struct spi_mem_dirmap_desc *desc,
+ u64 offs, size_t len, const void *buf)
+ {
+- struct spi_mem_op *op = &desc->info.op_tmpl;
+ struct spi_device *spi = desc->mem->spi;
+ u8 *txrx_buf = spi_get_ctldata(spi);
+ struct airoha_snand_ctrl *as_ctrl;
+ dma_addr_t dma_addr;
+- u32 wr_mode, val;
++ u32 wr_mode, val, opcode;
+ int err;
+
+ as_ctrl = spi_controller_get_devdata(spi->controller);
+
++ opcode = desc->info.op_tmpl.cmd.opcode;
++ switch (opcode) {
++ case SPI_NAND_OP_PROGRAM_LOAD_SINGLE:
++ case SPI_NAND_OP_PROGRAM_LOAD_RAMDOM_SINGLE:
++ wr_mode = 0;
++ break;
++ case SPI_NAND_OP_PROGRAM_LOAD_QUAD:
++ case SPI_NAND_OP_PROGRAM_LOAD_RAMDON_QUAD:
++ wr_mode = 2;
++ break;
++ default:
++ /* unknown opcode */
++ return -EOPNOTSUPP;
++ }
++
+ memcpy(txrx_buf + offs, buf, len);
+- dma_addr = dma_map_single(as_ctrl->dev, txrx_buf, SPI_NAND_CACHE_SIZE,
+- DMA_TO_DEVICE);
+- err = dma_mapping_error(as_ctrl->dev, dma_addr);
+- if (err)
+- return err;
+
+ err = airoha_snand_set_mode(as_ctrl, SPI_MODE_DMA);
+ if (err < 0)
+- goto error_dma_unmap;
++ return err;
+
+ err = airoha_snand_nfi_config(as_ctrl);
+ if (err)
+- goto error_dma_unmap;
++ goto error_dma_mode_off;
+
+- if (op->cmd.opcode == SPI_NAND_OP_PROGRAM_LOAD_QUAD ||
+- op->cmd.opcode == SPI_NAND_OP_PROGRAM_LOAD_RAMDON_QUAD)
+- wr_mode = BIT(1);
+- else
+- wr_mode = 0;
++ dma_addr = dma_map_single(as_ctrl->dev, txrx_buf, SPI_NAND_CACHE_SIZE,
++ DMA_TO_DEVICE);
++ err = dma_mapping_error(as_ctrl->dev, dma_addr);
++ if (err)
++ goto error_dma_mode_off;
+
++ /* set dma addr */
+ err = regmap_write(as_ctrl->regmap_nfi, REG_SPI_NFI_STRADDR,
+ dma_addr);
+ if (err)
+@@ -850,12 +861,13 @@ static ssize_t airoha_snand_dirmap_write
+ if (err)
+ goto error_dma_unmap;
+
++ /* set write command */
+ err = regmap_write(as_ctrl->regmap_nfi, REG_SPI_NFI_PG_CTL1,
+- FIELD_PREP(SPI_NFI_PG_LOAD_CMD,
+- op->cmd.opcode));
++ FIELD_PREP(SPI_NFI_PG_LOAD_CMD, opcode));
+ if (err)
+ goto error_dma_unmap;
+
++ /* set write mode */
+ err = regmap_write(as_ctrl->regmap_nfi, REG_SPI_NFI_SNF_MISC_CTL,
+ FIELD_PREP(SPI_NFI_DATA_READ_WR_MODE, wr_mode));
+ if (err)
+@@ -887,6 +899,7 @@ static ssize_t airoha_snand_dirmap_write
+ if (err)
+ goto error_dma_unmap;
+
++ /* trigger dma writing */
+ err = regmap_clear_bits(as_ctrl->regmap_nfi, REG_SPI_NFI_CON,
+ SPI_NFI_WR_TRIG);
+ if (err)
+@@ -931,6 +944,7 @@ static ssize_t airoha_snand_dirmap_write
+ error_dma_unmap:
+ dma_unmap_single(as_ctrl->dev, dma_addr, SPI_NAND_CACHE_SIZE,
+ DMA_TO_DEVICE);
++error_dma_mode_off:
+ airoha_snand_set_mode(as_ctrl, SPI_MODE_MANUAL);
+ return err;
+ }
+++ /dev/null
-From baaba9b8d3d907575323cbb7fabeae23db2a542b Mon Sep 17 00:00:00 2001
-From: Mikhail Kshevetskiy <mikhail.kshevetskiy@iopsys.eu>
-Date: Mon, 11 Aug 2025 20:52:34 +0300
-Subject: [PATCH v6 08/13] spi: airoha: support of dualio/quadio flash reading
- commands
-
-Airoha snfi spi controller supports acceleration of DUAL/QUAD
-operations, but does not supports DUAL_IO/QUAD_IO operations.
-Luckily DUAL/QUAD operations do the same as DUAL_IO/QUAD_IO ones,
-so we can issue corresponding DUAL/QUAD operation instead of
-DUAL_IO/QUAD_IO one.
-
-Signed-off-by: Mikhail Kshevetskiy <mikhail.kshevetskiy@iopsys.eu>
-Reviewed-by: AngeloGioacchino Del Regno <angelogioacchino.delregno@collabora.com>
----
- drivers/spi/spi-airoha-snfi.c | 28 ++++++++++++++++++++++------
- 1 file changed, 22 insertions(+), 6 deletions(-)
-
---- a/drivers/spi/spi-airoha-snfi.c
-+++ b/drivers/spi/spi-airoha-snfi.c
-@@ -147,6 +147,8 @@
- #define SPI_NFI_CUS_SEC_SIZE_EN BIT(16)
-
- #define REG_SPI_NFI_RD_CTL2 0x0510
-+#define SPI_NFI_DATA_READ_CMD GENMASK(7, 0)
-+
- #define REG_SPI_NFI_RD_CTL3 0x0514
-
- #define REG_SPI_NFI_PG_CTL1 0x0524
-@@ -179,7 +181,9 @@
- #define SPI_NAND_OP_READ_FROM_CACHE_SINGLE 0x03
- #define SPI_NAND_OP_READ_FROM_CACHE_SINGLE_FAST 0x0b
- #define SPI_NAND_OP_READ_FROM_CACHE_DUAL 0x3b
-+#define SPI_NAND_OP_READ_FROM_CACHE_DUALIO 0xbb
- #define SPI_NAND_OP_READ_FROM_CACHE_QUAD 0x6b
-+#define SPI_NAND_OP_READ_FROM_CACHE_QUADIO 0xeb
- #define SPI_NAND_OP_WRITE_ENABLE 0x06
- #define SPI_NAND_OP_WRITE_DISABLE 0x04
- #define SPI_NAND_OP_PROGRAM_LOAD_SINGLE 0x02
-@@ -664,26 +668,38 @@ static int airoha_snand_dirmap_create(st
- static ssize_t airoha_snand_dirmap_read(struct spi_mem_dirmap_desc *desc,
- u64 offs, size_t len, void *buf)
- {
-- struct spi_mem_op *op = &desc->info.op_tmpl;
- struct spi_device *spi = desc->mem->spi;
- struct airoha_snand_ctrl *as_ctrl;
- u8 *txrx_buf = spi_get_ctldata(spi);
- dma_addr_t dma_addr;
-- u32 val, rd_mode;
-+ u32 val, rd_mode, opcode;
- int err;
-
- as_ctrl = spi_controller_get_devdata(spi->controller);
-
-- switch (op->cmd.opcode) {
-+ /*
-+ * DUALIO and QUADIO opcodes are not supported by the spi controller,
-+ * replace them with supported opcodes.
-+ */
-+ opcode = desc->info.op_tmpl.cmd.opcode;
-+ switch (opcode) {
-+ case SPI_NAND_OP_READ_FROM_CACHE_SINGLE:
-+ case SPI_NAND_OP_READ_FROM_CACHE_SINGLE_FAST:
-+ rd_mode = 0;
-+ break;
- case SPI_NAND_OP_READ_FROM_CACHE_DUAL:
-+ case SPI_NAND_OP_READ_FROM_CACHE_DUALIO:
-+ opcode = SPI_NAND_OP_READ_FROM_CACHE_DUAL;
- rd_mode = 1;
- break;
- case SPI_NAND_OP_READ_FROM_CACHE_QUAD:
-+ case SPI_NAND_OP_READ_FROM_CACHE_QUADIO:
-+ opcode = SPI_NAND_OP_READ_FROM_CACHE_QUAD;
- rd_mode = 2;
- break;
- default:
-- rd_mode = 0;
-- break;
-+ /* unknown opcode */
-+ return -EOPNOTSUPP;
- }
-
- err = airoha_snand_set_mode(as_ctrl, SPI_MODE_DMA);
-@@ -717,7 +733,7 @@ static ssize_t airoha_snand_dirmap_read(
-
- /* set read command */
- err = regmap_write(as_ctrl->regmap_nfi, REG_SPI_NFI_RD_CTL2,
-- op->cmd.opcode);
-+ FIELD_PREP(SPI_NFI_DATA_READ_CMD, opcode));
- if (err)
- goto error_dma_unmap;
-
--- /dev/null
+From 80b09137aeab27e59004383058f8cc696a9ee048 Mon Sep 17 00:00:00 2001
+From: Mikhail Kshevetskiy <mikhail.kshevetskiy@iopsys.eu>
+Date: Sun, 12 Oct 2025 15:16:59 +0300
+Subject: [PATCH 08/14] spi: airoha: support of dualio/quadio flash reading
+ commands
+
+Airoha snfi spi controller supports acceleration of DUAL/QUAD
+operations, but does not supports DUAL_IO/QUAD_IO operations.
+Luckily DUAL/QUAD operations do the same as DUAL_IO/QUAD_IO ones,
+so we can issue corresponding DUAL/QUAD operation instead of
+DUAL_IO/QUAD_IO one.
+
+Signed-off-by: Mikhail Kshevetskiy <mikhail.kshevetskiy@iopsys.eu>
+Reviewed-by: AngeloGioacchino Del Regno <angelogioacchino.delregno@collabora.com>
+Link: https://patch.msgid.link/20251012121707.2296160-9-mikhail.kshevetskiy@iopsys.eu
+Signed-off-by: Mark Brown <broonie@kernel.org>
+---
+ drivers/spi/spi-airoha-snfi.c | 28 ++++++++++++++++++++++------
+ 1 file changed, 22 insertions(+), 6 deletions(-)
+
+--- a/drivers/spi/spi-airoha-snfi.c
++++ b/drivers/spi/spi-airoha-snfi.c
+@@ -147,6 +147,8 @@
+ #define SPI_NFI_CUS_SEC_SIZE_EN BIT(16)
+
+ #define REG_SPI_NFI_RD_CTL2 0x0510
++#define SPI_NFI_DATA_READ_CMD GENMASK(7, 0)
++
+ #define REG_SPI_NFI_RD_CTL3 0x0514
+
+ #define REG_SPI_NFI_PG_CTL1 0x0524
+@@ -179,7 +181,9 @@
+ #define SPI_NAND_OP_READ_FROM_CACHE_SINGLE 0x03
+ #define SPI_NAND_OP_READ_FROM_CACHE_SINGLE_FAST 0x0b
+ #define SPI_NAND_OP_READ_FROM_CACHE_DUAL 0x3b
++#define SPI_NAND_OP_READ_FROM_CACHE_DUALIO 0xbb
+ #define SPI_NAND_OP_READ_FROM_CACHE_QUAD 0x6b
++#define SPI_NAND_OP_READ_FROM_CACHE_QUADIO 0xeb
+ #define SPI_NAND_OP_WRITE_ENABLE 0x06
+ #define SPI_NAND_OP_WRITE_DISABLE 0x04
+ #define SPI_NAND_OP_PROGRAM_LOAD_SINGLE 0x02
+@@ -664,26 +668,38 @@ static int airoha_snand_dirmap_create(st
+ static ssize_t airoha_snand_dirmap_read(struct spi_mem_dirmap_desc *desc,
+ u64 offs, size_t len, void *buf)
+ {
+- struct spi_mem_op *op = &desc->info.op_tmpl;
+ struct spi_device *spi = desc->mem->spi;
+ struct airoha_snand_ctrl *as_ctrl;
+ u8 *txrx_buf = spi_get_ctldata(spi);
+ dma_addr_t dma_addr;
+- u32 val, rd_mode;
++ u32 val, rd_mode, opcode;
+ int err;
+
+ as_ctrl = spi_controller_get_devdata(spi->controller);
+
+- switch (op->cmd.opcode) {
++ /*
++ * DUALIO and QUADIO opcodes are not supported by the spi controller,
++ * replace them with supported opcodes.
++ */
++ opcode = desc->info.op_tmpl.cmd.opcode;
++ switch (opcode) {
++ case SPI_NAND_OP_READ_FROM_CACHE_SINGLE:
++ case SPI_NAND_OP_READ_FROM_CACHE_SINGLE_FAST:
++ rd_mode = 0;
++ break;
+ case SPI_NAND_OP_READ_FROM_CACHE_DUAL:
++ case SPI_NAND_OP_READ_FROM_CACHE_DUALIO:
++ opcode = SPI_NAND_OP_READ_FROM_CACHE_DUAL;
+ rd_mode = 1;
+ break;
+ case SPI_NAND_OP_READ_FROM_CACHE_QUAD:
++ case SPI_NAND_OP_READ_FROM_CACHE_QUADIO:
++ opcode = SPI_NAND_OP_READ_FROM_CACHE_QUAD;
+ rd_mode = 2;
+ break;
+ default:
+- rd_mode = 0;
+- break;
++ /* unknown opcode */
++ return -EOPNOTSUPP;
+ }
+
+ err = airoha_snand_set_mode(as_ctrl, SPI_MODE_DMA);
+@@ -717,7 +733,7 @@ static ssize_t airoha_snand_dirmap_read(
+
+ /* set read command */
+ err = regmap_write(as_ctrl->regmap_nfi, REG_SPI_NFI_RD_CTL2,
+- op->cmd.opcode);
++ FIELD_PREP(SPI_NFI_DATA_READ_CMD, opcode));
+ if (err)
+ goto error_dma_unmap;
+
+++ /dev/null
-From 6ca9cd453cb5d8a6411791295771b4dbd1c623de Mon Sep 17 00:00:00 2001
-From: Mikhail Kshevetskiy <mikhail.kshevetskiy@iopsys.eu>
-Date: Mon, 11 Aug 2025 21:18:04 +0300
-Subject: [PATCH v6 09/13] spi: airoha: buffer must be 0xff-ed before writing
-
-During writing, the entire flash page (including OOB) will be updated
-with the values from the temporary buffer, so we need to fill the
-untouched areas of the buffer with 0xff value to prevent accidental
-data overwriting.
-
-Signed-off-by: Mikhail Kshevetskiy <mikhail.kshevetskiy@iopsys.eu>
----
- drivers/spi/spi-airoha-snfi.c | 4 ++++
- 1 file changed, 4 insertions(+)
-
---- a/drivers/spi/spi-airoha-snfi.c
-+++ b/drivers/spi/spi-airoha-snfi.c
-@@ -847,7 +847,11 @@ static ssize_t airoha_snand_dirmap_write
- return -EOPNOTSUPP;
- }
-
-+ if (offs > 0)
-+ memset(txrx_buf, 0xff, offs);
- memcpy(txrx_buf + offs, buf, len);
-+ if (bytes > offs + len)
-+ memset(txrx_buf + offs + len, 0xff, bytes - offs - len);
-
- err = airoha_snand_set_mode(as_ctrl, SPI_MODE_DMA);
- if (err < 0)
--- /dev/null
+From 70eec454f2d6cdfab547c262781acd38328e11a1 Mon Sep 17 00:00:00 2001
+From: Mikhail Kshevetskiy <mikhail.kshevetskiy@iopsys.eu>
+Date: Sun, 12 Oct 2025 15:17:00 +0300
+Subject: [PATCH 09/14] spi: airoha: avoid setting of page/oob sizes in
+ REG_SPI_NFI_PAGEFMT
+
+spi-airoha-snfi uses custom sector size in REG_SPI_NFI_SECCUS_SIZE
+register, so setting of page/oob sizes in REG_SPI_NFI_PAGEFMT is not
+required.
+
+Signed-off-by: Mikhail Kshevetskiy <mikhail.kshevetskiy@iopsys.eu>
+Link: https://patch.msgid.link/20251012121707.2296160-10-mikhail.kshevetskiy@iopsys.eu
+Signed-off-by: Mark Brown <broonie@kernel.org>
+---
+ drivers/spi/spi-airoha-snfi.c | 38 -----------------------------------
+ 1 file changed, 38 deletions(-)
+
+--- a/drivers/spi/spi-airoha-snfi.c
++++ b/drivers/spi/spi-airoha-snfi.c
+@@ -518,44 +518,6 @@ static int airoha_snand_nfi_config(struc
+ if (err)
+ return err;
+
+- /* page format */
+- switch (as_ctrl->nfi_cfg.spare_size) {
+- case 26:
+- val = FIELD_PREP(SPI_NFI_SPARE_SIZE, 0x1);
+- break;
+- case 27:
+- val = FIELD_PREP(SPI_NFI_SPARE_SIZE, 0x2);
+- break;
+- case 28:
+- val = FIELD_PREP(SPI_NFI_SPARE_SIZE, 0x3);
+- break;
+- default:
+- val = FIELD_PREP(SPI_NFI_SPARE_SIZE, 0x0);
+- break;
+- }
+-
+- err = regmap_update_bits(as_ctrl->regmap_nfi, REG_SPI_NFI_PAGEFMT,
+- SPI_NFI_SPARE_SIZE, val);
+- if (err)
+- return err;
+-
+- switch (as_ctrl->nfi_cfg.page_size) {
+- case 2048:
+- val = FIELD_PREP(SPI_NFI_PAGE_SIZE, 0x1);
+- break;
+- case 4096:
+- val = FIELD_PREP(SPI_NFI_PAGE_SIZE, 0x2);
+- break;
+- default:
+- val = FIELD_PREP(SPI_NFI_PAGE_SIZE, 0x0);
+- break;
+- }
+-
+- err = regmap_update_bits(as_ctrl->regmap_nfi, REG_SPI_NFI_PAGEFMT,
+- SPI_NFI_PAGE_SIZE, val);
+- if (err)
+- return err;
+-
+ /* sec num */
+ val = FIELD_PREP(SPI_NFI_SEC_NUM, as_ctrl->nfi_cfg.sec_num);
+ err = regmap_update_bits(as_ctrl->regmap_nfi, REG_SPI_NFI_CON,
+++ /dev/null
-From 4abbbc74306598159fe1dc545f929ae594bf4dd1 Mon Sep 17 00:00:00 2001
-From: Mikhail Kshevetskiy <mikhail.kshevetskiy@iopsys.eu>
-Date: Thu, 14 Aug 2025 18:00:32 +0300
-Subject: [PATCH v6 10/13] spi: airoha: avoid setting of page/oob sizes in
- REG_SPI_NFI_PAGEFMT
-
-spi-airoha-snfi uses custom sector size in REG_SPI_NFI_SECCUS_SIZE
-register, so setting of page/oob sizes in REG_SPI_NFI_PAGEFMT is not
-required.
-
-Signed-off-by: Mikhail Kshevetskiy <mikhail.kshevetskiy@iopsys.eu>
----
- drivers/spi/spi-airoha-snfi.c | 38 -----------------------------------
- 1 file changed, 38 deletions(-)
-
---- a/drivers/spi/spi-airoha-snfi.c
-+++ b/drivers/spi/spi-airoha-snfi.c
-@@ -518,44 +518,6 @@ static int airoha_snand_nfi_config(struc
- if (err)
- return err;
-
-- /* page format */
-- switch (as_ctrl->nfi_cfg.spare_size) {
-- case 26:
-- val = FIELD_PREP(SPI_NFI_SPARE_SIZE, 0x1);
-- break;
-- case 27:
-- val = FIELD_PREP(SPI_NFI_SPARE_SIZE, 0x2);
-- break;
-- case 28:
-- val = FIELD_PREP(SPI_NFI_SPARE_SIZE, 0x3);
-- break;
-- default:
-- val = FIELD_PREP(SPI_NFI_SPARE_SIZE, 0x0);
-- break;
-- }
--
-- err = regmap_update_bits(as_ctrl->regmap_nfi, REG_SPI_NFI_PAGEFMT,
-- SPI_NFI_SPARE_SIZE, val);
-- if (err)
-- return err;
--
-- switch (as_ctrl->nfi_cfg.page_size) {
-- case 2048:
-- val = FIELD_PREP(SPI_NFI_PAGE_SIZE, 0x1);
-- break;
-- case 4096:
-- val = FIELD_PREP(SPI_NFI_PAGE_SIZE, 0x2);
-- break;
-- default:
-- val = FIELD_PREP(SPI_NFI_PAGE_SIZE, 0x0);
-- break;
-- }
--
-- err = regmap_update_bits(as_ctrl->regmap_nfi, REG_SPI_NFI_PAGEFMT,
-- SPI_NFI_PAGE_SIZE, val);
-- if (err)
-- return err;
--
- /* sec num */
- val = FIELD_PREP(SPI_NFI_SEC_NUM, as_ctrl->nfi_cfg.sec_num);
- err = regmap_update_bits(as_ctrl->regmap_nfi, REG_SPI_NFI_CON,
--- /dev/null
+From d1ff30df1d9a4eb4c067795abb5e2a66910fd108 Mon Sep 17 00:00:00 2001
+From: Mikhail Kshevetskiy <mikhail.kshevetskiy@iopsys.eu>
+Date: Sun, 12 Oct 2025 15:17:01 +0300
+Subject: [PATCH 10/14] spi: airoha: reduce the number of modification of
+ REG_SPI_NFI_CNFG and REG_SPI_NFI_SECCUS_SIZE registers
+
+This just reduce the number of modification of REG_SPI_NFI_CNFG and
+REG_SPI_NFI_SECCUS_SIZE registers during dirmap operation.
+
+This patch is a necessary step to avoid reading flash page settings
+from SNFI registers during driver startup.
+
+Signed-off-by: Mikhail Kshevetskiy <mikhail.kshevetskiy@iopsys.eu>
+Reviewed-by: AngeloGioacchino Del Regno <angelogioacchino.delregno@collabora.com>
+Link: https://patch.msgid.link/20251012121707.2296160-11-mikhail.kshevetskiy@iopsys.eu
+Signed-off-by: Mark Brown <broonie@kernel.org>
+---
+ drivers/spi/spi-airoha-snfi.c | 135 +++++++++++++++++++++++++---------
+ 1 file changed, 102 insertions(+), 33 deletions(-)
+
+--- a/drivers/spi/spi-airoha-snfi.c
++++ b/drivers/spi/spi-airoha-snfi.c
+@@ -668,7 +668,48 @@ static ssize_t airoha_snand_dirmap_read(
+ if (err < 0)
+ return err;
+
+- err = airoha_snand_nfi_config(as_ctrl);
++ /* NFI reset */
++ err = regmap_write(as_ctrl->regmap_nfi, REG_SPI_NFI_CON,
++ SPI_NFI_FIFO_FLUSH | SPI_NFI_RST);
++ if (err)
++ goto error_dma_mode_off;
++
++ /* NFI configure:
++ * - No AutoFDM (custom sector size (SECCUS) register will be used)
++ * - No SoC's hardware ECC (flash internal ECC will be used)
++ * - Use burst mode (faster, but requires 16 byte alignment for addresses)
++ * - Setup for reading (SPI_NFI_READ_MODE)
++ * - Setup reading command: FIELD_PREP(SPI_NFI_OPMODE, 6)
++ * - Use DMA instead of PIO for data reading
++ */
++ err = regmap_update_bits(as_ctrl->regmap_nfi, REG_SPI_NFI_CNFG,
++ SPI_NFI_DMA_MODE |
++ SPI_NFI_READ_MODE |
++ SPI_NFI_DMA_BURST_EN |
++ SPI_NFI_HW_ECC_EN |
++ SPI_NFI_AUTO_FDM_EN |
++ SPI_NFI_OPMODE,
++ SPI_NFI_DMA_MODE |
++ SPI_NFI_READ_MODE |
++ SPI_NFI_DMA_BURST_EN |
++ FIELD_PREP(SPI_NFI_OPMODE, 6));
++ if (err)
++ goto error_dma_mode_off;
++
++ /* Set number of sector will be read */
++ val = FIELD_PREP(SPI_NFI_SEC_NUM, as_ctrl->nfi_cfg.sec_num);
++ err = regmap_update_bits(as_ctrl->regmap_nfi, REG_SPI_NFI_CON,
++ SPI_NFI_SEC_NUM, val);
++ if (err)
++ goto error_dma_mode_off;
++
++ /* Set custom sector size */
++ val = as_ctrl->nfi_cfg.sec_size;
++ err = regmap_update_bits(as_ctrl->regmap_nfi, REG_SPI_NFI_SECCUS_SIZE,
++ SPI_NFI_CUS_SEC_SIZE |
++ SPI_NFI_CUS_SEC_SIZE_EN,
++ FIELD_PREP(SPI_NFI_CUS_SEC_SIZE, val) |
++ SPI_NFI_CUS_SEC_SIZE_EN);
+ if (err)
+ goto error_dma_mode_off;
+
+@@ -684,7 +725,14 @@ static ssize_t airoha_snand_dirmap_read(
+ if (err)
+ goto error_dma_unmap;
+
+- /* set cust sec size */
++ /*
++ * Setup transfer length
++ * ---------------------
++ * The following rule MUST be met:
++ * transfer_length =
++ * = NFI_SNF_MISC_CTL2.read_data_byte_number =
++ * = NFI_CON.sector_number * NFI_SECCUS.custom_sector_size
++ */
+ val = as_ctrl->nfi_cfg.sec_size * as_ctrl->nfi_cfg.sec_num;
+ val = FIELD_PREP(SPI_NFI_READ_DATA_BYTE_NUM, val);
+ err = regmap_update_bits(as_ctrl->regmap_nfi,
+@@ -711,18 +759,6 @@ static ssize_t airoha_snand_dirmap_read(
+ if (err)
+ goto error_dma_unmap;
+
+- /* set nfi read */
+- err = regmap_update_bits(as_ctrl->regmap_nfi, REG_SPI_NFI_CNFG,
+- SPI_NFI_OPMODE,
+- FIELD_PREP(SPI_NFI_OPMODE, 6));
+- if (err)
+- goto error_dma_unmap;
+-
+- err = regmap_set_bits(as_ctrl->regmap_nfi, REG_SPI_NFI_CNFG,
+- SPI_NFI_READ_MODE | SPI_NFI_DMA_MODE);
+- if (err)
+- goto error_dma_unmap;
+-
+ err = regmap_write(as_ctrl->regmap_nfi, REG_SPI_NFI_CMD, 0x0);
+ if (err)
+ goto error_dma_unmap;
+@@ -815,7 +851,48 @@ static ssize_t airoha_snand_dirmap_write
+ if (err < 0)
+ return err;
+
+- err = airoha_snand_nfi_config(as_ctrl);
++ /* NFI reset */
++ err = regmap_write(as_ctrl->regmap_nfi, REG_SPI_NFI_CON,
++ SPI_NFI_FIFO_FLUSH | SPI_NFI_RST);
++ if (err)
++ goto error_dma_mode_off;
++
++ /*
++ * NFI configure:
++ * - No AutoFDM (custom sector size (SECCUS) register will be used)
++ * - No SoC's hardware ECC (flash internal ECC will be used)
++ * - Use burst mode (faster, but requires 16 byte alignment for addresses)
++ * - Setup for writing (SPI_NFI_READ_MODE bit is cleared)
++ * - Setup writing command: FIELD_PREP(SPI_NFI_OPMODE, 3)
++ * - Use DMA instead of PIO for data writing
++ */
++ err = regmap_update_bits(as_ctrl->regmap_nfi, REG_SPI_NFI_CNFG,
++ SPI_NFI_DMA_MODE |
++ SPI_NFI_READ_MODE |
++ SPI_NFI_DMA_BURST_EN |
++ SPI_NFI_HW_ECC_EN |
++ SPI_NFI_AUTO_FDM_EN |
++ SPI_NFI_OPMODE,
++ SPI_NFI_DMA_MODE |
++ SPI_NFI_DMA_BURST_EN |
++ FIELD_PREP(SPI_NFI_OPMODE, 3));
++ if (err)
++ goto error_dma_mode_off;
++
++ /* Set number of sector will be written */
++ val = FIELD_PREP(SPI_NFI_SEC_NUM, as_ctrl->nfi_cfg.sec_num);
++ err = regmap_update_bits(as_ctrl->regmap_nfi, REG_SPI_NFI_CON,
++ SPI_NFI_SEC_NUM, val);
++ if (err)
++ goto error_dma_mode_off;
++
++ /* Set custom sector size */
++ val = as_ctrl->nfi_cfg.sec_size;
++ err = regmap_update_bits(as_ctrl->regmap_nfi, REG_SPI_NFI_SECCUS_SIZE,
++ SPI_NFI_CUS_SEC_SIZE |
++ SPI_NFI_CUS_SEC_SIZE_EN,
++ FIELD_PREP(SPI_NFI_CUS_SEC_SIZE, val) |
++ SPI_NFI_CUS_SEC_SIZE_EN);
+ if (err)
+ goto error_dma_mode_off;
+
+@@ -831,8 +908,16 @@ static ssize_t airoha_snand_dirmap_write
+ if (err)
+ goto error_dma_unmap;
+
+- val = FIELD_PREP(SPI_NFI_PROG_LOAD_BYTE_NUM,
+- as_ctrl->nfi_cfg.sec_size * as_ctrl->nfi_cfg.sec_num);
++ /*
++ * Setup transfer length
++ * ---------------------
++ * The following rule MUST be met:
++ * transfer_length =
++ * = NFI_SNF_MISC_CTL2.write_data_byte_number =
++ * = NFI_CON.sector_number * NFI_SECCUS.custom_sector_size
++ */
++ val = as_ctrl->nfi_cfg.sec_size * as_ctrl->nfi_cfg.sec_num;
++ val = FIELD_PREP(SPI_NFI_PROG_LOAD_BYTE_NUM, val);
+ err = regmap_update_bits(as_ctrl->regmap_nfi,
+ REG_SPI_NFI_SNF_MISC_CTL2,
+ SPI_NFI_PROG_LOAD_BYTE_NUM, val);
+@@ -857,22 +942,6 @@ static ssize_t airoha_snand_dirmap_write
+ if (err)
+ goto error_dma_unmap;
+
+- err = regmap_clear_bits(as_ctrl->regmap_nfi, REG_SPI_NFI_CNFG,
+- SPI_NFI_READ_MODE);
+- if (err)
+- goto error_dma_unmap;
+-
+- err = regmap_update_bits(as_ctrl->regmap_nfi, REG_SPI_NFI_CNFG,
+- SPI_NFI_OPMODE,
+- FIELD_PREP(SPI_NFI_OPMODE, 3));
+- if (err)
+- goto error_dma_unmap;
+-
+- err = regmap_set_bits(as_ctrl->regmap_nfi, REG_SPI_NFI_CNFG,
+- SPI_NFI_DMA_MODE);
+- if (err)
+- goto error_dma_unmap;
+-
+ err = regmap_write(as_ctrl->regmap_nfi, REG_SPI_NFI_CMD, 0x80);
+ if (err)
+ goto error_dma_unmap;
+++ /dev/null
-From 0d8f58869192df0acdba286d233b57a4feeaf94b Mon Sep 17 00:00:00 2001
-From: Mikhail Kshevetskiy <mikhail.kshevetskiy@iopsys.eu>
-Date: Thu, 14 Aug 2025 18:49:34 +0300
-Subject: [PATCH v6 11/13] spi: airoha: reduce the number of modification of
- REG_SPI_NFI_CNFG and REG_SPI_NFI_SECCUS_SIZE registers
-
-This just reduce the number of modification of REG_SPI_NFI_CNFG and
-REG_SPI_NFI_SECCUS_SIZE registers during dirmap operation.
-
-This patch is a necessary step to avoid reading flash page settings
-from SNFI registers during driver startup.
-
-Signed-off-by: Mikhail Kshevetskiy <mikhail.kshevetskiy@iopsys.eu>
-Reviewed-by: AngeloGioacchino Del Regno <angelogioacchino.delregno@collabora.com>
----
- drivers/spi/spi-airoha-snfi.c | 135 +++++++++++++++++++++++++---------
- 1 file changed, 102 insertions(+), 33 deletions(-)
-
---- a/drivers/spi/spi-airoha-snfi.c
-+++ b/drivers/spi/spi-airoha-snfi.c
-@@ -668,7 +668,48 @@ static ssize_t airoha_snand_dirmap_read(
- if (err < 0)
- return err;
-
-- err = airoha_snand_nfi_config(as_ctrl);
-+ /* NFI reset */
-+ err = regmap_write(as_ctrl->regmap_nfi, REG_SPI_NFI_CON,
-+ SPI_NFI_FIFO_FLUSH | SPI_NFI_RST);
-+ if (err)
-+ goto error_dma_mode_off;
-+
-+ /* NFI configure:
-+ * - No AutoFDM (custom sector size (SECCUS) register will be used)
-+ * - No SoC's hardware ECC (flash internal ECC will be used)
-+ * - Use burst mode (faster, but requires 16 byte alignment for addresses)
-+ * - Setup for reading (SPI_NFI_READ_MODE)
-+ * - Setup reading command: FIELD_PREP(SPI_NFI_OPMODE, 6)
-+ * - Use DMA instead of PIO for data reading
-+ */
-+ err = regmap_update_bits(as_ctrl->regmap_nfi, REG_SPI_NFI_CNFG,
-+ SPI_NFI_DMA_MODE |
-+ SPI_NFI_READ_MODE |
-+ SPI_NFI_DMA_BURST_EN |
-+ SPI_NFI_HW_ECC_EN |
-+ SPI_NFI_AUTO_FDM_EN |
-+ SPI_NFI_OPMODE,
-+ SPI_NFI_DMA_MODE |
-+ SPI_NFI_READ_MODE |
-+ SPI_NFI_DMA_BURST_EN |
-+ FIELD_PREP(SPI_NFI_OPMODE, 6));
-+ if (err)
-+ goto error_dma_mode_off;
-+
-+ /* Set number of sector will be read */
-+ val = FIELD_PREP(SPI_NFI_SEC_NUM, as_ctrl->nfi_cfg.sec_num);
-+ err = regmap_update_bits(as_ctrl->regmap_nfi, REG_SPI_NFI_CON,
-+ SPI_NFI_SEC_NUM, val);
-+ if (err)
-+ goto error_dma_mode_off;
-+
-+ /* Set custom sector size */
-+ val = as_ctrl->nfi_cfg.sec_size;
-+ err = regmap_update_bits(as_ctrl->regmap_nfi, REG_SPI_NFI_SECCUS_SIZE,
-+ SPI_NFI_CUS_SEC_SIZE |
-+ SPI_NFI_CUS_SEC_SIZE_EN,
-+ FIELD_PREP(SPI_NFI_CUS_SEC_SIZE, val) |
-+ SPI_NFI_CUS_SEC_SIZE_EN);
- if (err)
- goto error_dma_mode_off;
-
-@@ -684,7 +725,14 @@ static ssize_t airoha_snand_dirmap_read(
- if (err)
- goto error_dma_unmap;
-
-- /* set cust sec size */
-+ /*
-+ * Setup transfer length
-+ * ---------------------
-+ * The following rule MUST be met:
-+ * transfer_length =
-+ * = NFI_SNF_MISC_CTL2.read_data_byte_number =
-+ * = NFI_CON.sector_number * NFI_SECCUS.custom_sector_size
-+ */
- val = as_ctrl->nfi_cfg.sec_size * as_ctrl->nfi_cfg.sec_num;
- val = FIELD_PREP(SPI_NFI_READ_DATA_BYTE_NUM, val);
- err = regmap_update_bits(as_ctrl->regmap_nfi,
-@@ -711,18 +759,6 @@ static ssize_t airoha_snand_dirmap_read(
- if (err)
- goto error_dma_unmap;
-
-- /* set nfi read */
-- err = regmap_update_bits(as_ctrl->regmap_nfi, REG_SPI_NFI_CNFG,
-- SPI_NFI_OPMODE,
-- FIELD_PREP(SPI_NFI_OPMODE, 6));
-- if (err)
-- goto error_dma_unmap;
--
-- err = regmap_set_bits(as_ctrl->regmap_nfi, REG_SPI_NFI_CNFG,
-- SPI_NFI_READ_MODE | SPI_NFI_DMA_MODE);
-- if (err)
-- goto error_dma_unmap;
--
- err = regmap_write(as_ctrl->regmap_nfi, REG_SPI_NFI_CMD, 0x0);
- if (err)
- goto error_dma_unmap;
-@@ -819,7 +855,48 @@ static ssize_t airoha_snand_dirmap_write
- if (err < 0)
- return err;
-
-- err = airoha_snand_nfi_config(as_ctrl);
-+ /* NFI reset */
-+ err = regmap_write(as_ctrl->regmap_nfi, REG_SPI_NFI_CON,
-+ SPI_NFI_FIFO_FLUSH | SPI_NFI_RST);
-+ if (err)
-+ goto error_dma_mode_off;
-+
-+ /*
-+ * NFI configure:
-+ * - No AutoFDM (custom sector size (SECCUS) register will be used)
-+ * - No SoC's hardware ECC (flash internal ECC will be used)
-+ * - Use burst mode (faster, but requires 16 byte alignment for addresses)
-+ * - Setup for writing (SPI_NFI_READ_MODE bit is cleared)
-+ * - Setup writing command: FIELD_PREP(SPI_NFI_OPMODE, 3)
-+ * - Use DMA instead of PIO for data writing
-+ */
-+ err = regmap_update_bits(as_ctrl->regmap_nfi, REG_SPI_NFI_CNFG,
-+ SPI_NFI_DMA_MODE |
-+ SPI_NFI_READ_MODE |
-+ SPI_NFI_DMA_BURST_EN |
-+ SPI_NFI_HW_ECC_EN |
-+ SPI_NFI_AUTO_FDM_EN |
-+ SPI_NFI_OPMODE,
-+ SPI_NFI_DMA_MODE |
-+ SPI_NFI_DMA_BURST_EN |
-+ FIELD_PREP(SPI_NFI_OPMODE, 3));
-+ if (err)
-+ goto error_dma_mode_off;
-+
-+ /* Set number of sector will be written */
-+ val = FIELD_PREP(SPI_NFI_SEC_NUM, as_ctrl->nfi_cfg.sec_num);
-+ err = regmap_update_bits(as_ctrl->regmap_nfi, REG_SPI_NFI_CON,
-+ SPI_NFI_SEC_NUM, val);
-+ if (err)
-+ goto error_dma_mode_off;
-+
-+ /* Set custom sector size */
-+ val = as_ctrl->nfi_cfg.sec_size;
-+ err = regmap_update_bits(as_ctrl->regmap_nfi, REG_SPI_NFI_SECCUS_SIZE,
-+ SPI_NFI_CUS_SEC_SIZE |
-+ SPI_NFI_CUS_SEC_SIZE_EN,
-+ FIELD_PREP(SPI_NFI_CUS_SEC_SIZE, val) |
-+ SPI_NFI_CUS_SEC_SIZE_EN);
- if (err)
- goto error_dma_mode_off;
-
-@@ -835,8 +912,16 @@ static ssize_t airoha_snand_dirmap_write
- if (err)
- goto error_dma_unmap;
-
-- val = FIELD_PREP(SPI_NFI_PROG_LOAD_BYTE_NUM,
-- as_ctrl->nfi_cfg.sec_size * as_ctrl->nfi_cfg.sec_num);
-+ /*
-+ * Setup transfer length
-+ * ---------------------
-+ * The following rule MUST be met:
-+ * transfer_length =
-+ * = NFI_SNF_MISC_CTL2.write_data_byte_number =
-+ * = NFI_CON.sector_number * NFI_SECCUS.custom_sector_size
-+ */
-+ val = as_ctrl->nfi_cfg.sec_size * as_ctrl->nfi_cfg.sec_num;
-+ val = FIELD_PREP(SPI_NFI_PROG_LOAD_BYTE_NUM, val);
- err = regmap_update_bits(as_ctrl->regmap_nfi,
- REG_SPI_NFI_SNF_MISC_CTL2,
- SPI_NFI_PROG_LOAD_BYTE_NUM, val);
-@@ -861,22 +946,6 @@ static ssize_t airoha_snand_dirmap_write
- if (err)
- goto error_dma_unmap;
-
-- err = regmap_clear_bits(as_ctrl->regmap_nfi, REG_SPI_NFI_CNFG,
-- SPI_NFI_READ_MODE);
-- if (err)
-- goto error_dma_unmap;
--
-- err = regmap_update_bits(as_ctrl->regmap_nfi, REG_SPI_NFI_CNFG,
-- SPI_NFI_OPMODE,
-- FIELD_PREP(SPI_NFI_OPMODE, 3));
-- if (err)
-- goto error_dma_unmap;
--
-- err = regmap_set_bits(as_ctrl->regmap_nfi, REG_SPI_NFI_CNFG,
-- SPI_NFI_DMA_MODE);
-- if (err)
-- goto error_dma_unmap;
--
- err = regmap_write(as_ctrl->regmap_nfi, REG_SPI_NFI_CMD, 0x80);
- if (err)
- goto error_dma_unmap;
--- /dev/null
+From fb81b5cecb8553e3ca2b45288cf340d43c9c2991 Mon Sep 17 00:00:00 2001
+From: Mikhail Kshevetskiy <mikhail.kshevetskiy@iopsys.eu>
+Date: Sun, 12 Oct 2025 15:17:02 +0300
+Subject: [PATCH 11/14] spi: airoha: set custom sector size equal to flash page
+ size
+
+Set custom sector size equal to flash page size including oob. Thus we
+will always read a single sector. The maximum custom sector size is
+8187, so all possible flash sector sizes are supported.
+
+This patch is a necessary step to avoid reading flash page settings
+from SNFI registers during driver startup.
+
+Signed-off-by: Mikhail Kshevetskiy <mikhail.kshevetskiy@iopsys.eu>
+Reviewed-by: AngeloGioacchino Del Regno <angelogioacchino.delregno@collabora.com>
+Link: https://patch.msgid.link/20251012121707.2296160-12-mikhail.kshevetskiy@iopsys.eu
+Signed-off-by: Mark Brown <broonie@kernel.org>
+---
+ drivers/spi/spi-airoha-snfi.c | 35 +++++++++++++++++++----------------
+ 1 file changed, 19 insertions(+), 16 deletions(-)
+
+--- a/drivers/spi/spi-airoha-snfi.c
++++ b/drivers/spi/spi-airoha-snfi.c
+@@ -519,7 +519,7 @@ static int airoha_snand_nfi_config(struc
+ return err;
+
+ /* sec num */
+- val = FIELD_PREP(SPI_NFI_SEC_NUM, as_ctrl->nfi_cfg.sec_num);
++ val = FIELD_PREP(SPI_NFI_SEC_NUM, 1);
+ err = regmap_update_bits(as_ctrl->regmap_nfi, REG_SPI_NFI_CON,
+ SPI_NFI_SEC_NUM, val);
+ if (err)
+@@ -532,7 +532,8 @@ static int airoha_snand_nfi_config(struc
+ return err;
+
+ /* set cust sec size */
+- val = FIELD_PREP(SPI_NFI_CUS_SEC_SIZE, as_ctrl->nfi_cfg.sec_size);
++ val = FIELD_PREP(SPI_NFI_CUS_SEC_SIZE,
++ as_ctrl->nfi_cfg.sec_size * as_ctrl->nfi_cfg.sec_num);
+ return regmap_update_bits(as_ctrl->regmap_nfi,
+ REG_SPI_NFI_SECCUS_SIZE,
+ SPI_NFI_CUS_SEC_SIZE, val);
+@@ -635,10 +636,13 @@ static ssize_t airoha_snand_dirmap_read(
+ u8 *txrx_buf = spi_get_ctldata(spi);
+ dma_addr_t dma_addr;
+ u32 val, rd_mode, opcode;
++ size_t bytes;
+ int err;
+
+ as_ctrl = spi_controller_get_devdata(spi->controller);
+
++ bytes = as_ctrl->nfi_cfg.sec_num * as_ctrl->nfi_cfg.sec_size;
++
+ /*
+ * DUALIO and QUADIO opcodes are not supported by the spi controller,
+ * replace them with supported opcodes.
+@@ -697,18 +701,17 @@ static ssize_t airoha_snand_dirmap_read(
+ goto error_dma_mode_off;
+
+ /* Set number of sector will be read */
+- val = FIELD_PREP(SPI_NFI_SEC_NUM, as_ctrl->nfi_cfg.sec_num);
+ err = regmap_update_bits(as_ctrl->regmap_nfi, REG_SPI_NFI_CON,
+- SPI_NFI_SEC_NUM, val);
++ SPI_NFI_SEC_NUM,
++ FIELD_PREP(SPI_NFI_SEC_NUM, 1));
+ if (err)
+ goto error_dma_mode_off;
+
+ /* Set custom sector size */
+- val = as_ctrl->nfi_cfg.sec_size;
+ err = regmap_update_bits(as_ctrl->regmap_nfi, REG_SPI_NFI_SECCUS_SIZE,
+ SPI_NFI_CUS_SEC_SIZE |
+ SPI_NFI_CUS_SEC_SIZE_EN,
+- FIELD_PREP(SPI_NFI_CUS_SEC_SIZE, val) |
++ FIELD_PREP(SPI_NFI_CUS_SEC_SIZE, bytes) |
+ SPI_NFI_CUS_SEC_SIZE_EN);
+ if (err)
+ goto error_dma_mode_off;
+@@ -733,11 +736,10 @@ static ssize_t airoha_snand_dirmap_read(
+ * = NFI_SNF_MISC_CTL2.read_data_byte_number =
+ * = NFI_CON.sector_number * NFI_SECCUS.custom_sector_size
+ */
+- val = as_ctrl->nfi_cfg.sec_size * as_ctrl->nfi_cfg.sec_num;
+- val = FIELD_PREP(SPI_NFI_READ_DATA_BYTE_NUM, val);
+ err = regmap_update_bits(as_ctrl->regmap_nfi,
+ REG_SPI_NFI_SNF_MISC_CTL2,
+- SPI_NFI_READ_DATA_BYTE_NUM, val);
++ SPI_NFI_READ_DATA_BYTE_NUM,
++ FIELD_PREP(SPI_NFI_READ_DATA_BYTE_NUM, bytes));
+ if (err)
+ goto error_dma_unmap;
+
+@@ -826,10 +828,13 @@ static ssize_t airoha_snand_dirmap_write
+ struct airoha_snand_ctrl *as_ctrl;
+ dma_addr_t dma_addr;
+ u32 wr_mode, val, opcode;
++ size_t bytes;
+ int err;
+
+ as_ctrl = spi_controller_get_devdata(spi->controller);
+
++ bytes = as_ctrl->nfi_cfg.sec_num * as_ctrl->nfi_cfg.sec_size;
++
+ opcode = desc->info.op_tmpl.cmd.opcode;
+ switch (opcode) {
+ case SPI_NAND_OP_PROGRAM_LOAD_SINGLE:
+@@ -880,18 +885,17 @@ static ssize_t airoha_snand_dirmap_write
+ goto error_dma_mode_off;
+
+ /* Set number of sector will be written */
+- val = FIELD_PREP(SPI_NFI_SEC_NUM, as_ctrl->nfi_cfg.sec_num);
+ err = regmap_update_bits(as_ctrl->regmap_nfi, REG_SPI_NFI_CON,
+- SPI_NFI_SEC_NUM, val);
++ SPI_NFI_SEC_NUM,
++ FIELD_PREP(SPI_NFI_SEC_NUM, 1));
+ if (err)
+ goto error_dma_mode_off;
+
+ /* Set custom sector size */
+- val = as_ctrl->nfi_cfg.sec_size;
+ err = regmap_update_bits(as_ctrl->regmap_nfi, REG_SPI_NFI_SECCUS_SIZE,
+ SPI_NFI_CUS_SEC_SIZE |
+ SPI_NFI_CUS_SEC_SIZE_EN,
+- FIELD_PREP(SPI_NFI_CUS_SEC_SIZE, val) |
++ FIELD_PREP(SPI_NFI_CUS_SEC_SIZE, bytes) |
+ SPI_NFI_CUS_SEC_SIZE_EN);
+ if (err)
+ goto error_dma_mode_off;
+@@ -916,11 +920,10 @@ static ssize_t airoha_snand_dirmap_write
+ * = NFI_SNF_MISC_CTL2.write_data_byte_number =
+ * = NFI_CON.sector_number * NFI_SECCUS.custom_sector_size
+ */
+- val = as_ctrl->nfi_cfg.sec_size * as_ctrl->nfi_cfg.sec_num;
+- val = FIELD_PREP(SPI_NFI_PROG_LOAD_BYTE_NUM, val);
+ err = regmap_update_bits(as_ctrl->regmap_nfi,
+ REG_SPI_NFI_SNF_MISC_CTL2,
+- SPI_NFI_PROG_LOAD_BYTE_NUM, val);
++ SPI_NFI_PROG_LOAD_BYTE_NUM,
++ FIELD_PREP(SPI_NFI_PROG_LOAD_BYTE_NUM, bytes));
+ if (err)
+ goto error_dma_unmap;
+
+++ /dev/null
-From 893ee23d650ca9ee36541b9a5ae0bc18be01a11f Mon Sep 17 00:00:00 2001
-From: Mikhail Kshevetskiy <mikhail.kshevetskiy@iopsys.eu>
-Date: Thu, 14 Aug 2025 22:47:17 +0300
-Subject: [PATCH v6 12/13] spi: airoha: set custom sector size equal to flash
- page size
-
-Set custom sector size equal to flash page size including oob. Thus we
-will always read a single sector. The maximum custom sector size is
-8187, so all possible flash sector sizes are supported.
-
-This patch is a necessary step to avoid reading flash page settings
-from SNFI registers during driver startup.
-
-Signed-off-by: Mikhail Kshevetskiy <mikhail.kshevetskiy@iopsys.eu>
-Reviewed-by: AngeloGioacchino Del Regno <angelogioacchino.delregno@collabora.com>
----
- drivers/spi/spi-airoha-snfi.c | 35 +++++++++++++++++++----------------
- 1 file changed, 19 insertions(+), 16 deletions(-)
-
---- a/drivers/spi/spi-airoha-snfi.c
-+++ b/drivers/spi/spi-airoha-snfi.c
-@@ -519,7 +519,7 @@ static int airoha_snand_nfi_config(struc
- return err;
-
- /* sec num */
-- val = FIELD_PREP(SPI_NFI_SEC_NUM, as_ctrl->nfi_cfg.sec_num);
-+ val = FIELD_PREP(SPI_NFI_SEC_NUM, 1);
- err = regmap_update_bits(as_ctrl->regmap_nfi, REG_SPI_NFI_CON,
- SPI_NFI_SEC_NUM, val);
- if (err)
-@@ -532,7 +532,8 @@ static int airoha_snand_nfi_config(struc
- return err;
-
- /* set cust sec size */
-- val = FIELD_PREP(SPI_NFI_CUS_SEC_SIZE, as_ctrl->nfi_cfg.sec_size);
-+ val = FIELD_PREP(SPI_NFI_CUS_SEC_SIZE,
-+ as_ctrl->nfi_cfg.sec_size * as_ctrl->nfi_cfg.sec_num);
- return regmap_update_bits(as_ctrl->regmap_nfi,
- REG_SPI_NFI_SECCUS_SIZE,
- SPI_NFI_CUS_SEC_SIZE, val);
-@@ -635,10 +636,13 @@ static ssize_t airoha_snand_dirmap_read(
- u8 *txrx_buf = spi_get_ctldata(spi);
- dma_addr_t dma_addr;
- u32 val, rd_mode, opcode;
-+ size_t bytes;
- int err;
-
- as_ctrl = spi_controller_get_devdata(spi->controller);
-
-+ bytes = as_ctrl->nfi_cfg.sec_num * as_ctrl->nfi_cfg.sec_size;
-+
- /*
- * DUALIO and QUADIO opcodes are not supported by the spi controller,
- * replace them with supported opcodes.
-@@ -697,18 +701,17 @@ static ssize_t airoha_snand_dirmap_read(
- goto error_dma_mode_off;
-
- /* Set number of sector will be read */
-- val = FIELD_PREP(SPI_NFI_SEC_NUM, as_ctrl->nfi_cfg.sec_num);
- err = regmap_update_bits(as_ctrl->regmap_nfi, REG_SPI_NFI_CON,
-- SPI_NFI_SEC_NUM, val);
-+ SPI_NFI_SEC_NUM,
-+ FIELD_PREP(SPI_NFI_SEC_NUM, 1));
- if (err)
- goto error_dma_mode_off;
-
- /* Set custom sector size */
-- val = as_ctrl->nfi_cfg.sec_size;
- err = regmap_update_bits(as_ctrl->regmap_nfi, REG_SPI_NFI_SECCUS_SIZE,
- SPI_NFI_CUS_SEC_SIZE |
- SPI_NFI_CUS_SEC_SIZE_EN,
-- FIELD_PREP(SPI_NFI_CUS_SEC_SIZE, val) |
-+ FIELD_PREP(SPI_NFI_CUS_SEC_SIZE, bytes) |
- SPI_NFI_CUS_SEC_SIZE_EN);
- if (err)
- goto error_dma_mode_off;
-@@ -733,11 +736,10 @@ static ssize_t airoha_snand_dirmap_read(
- * = NFI_SNF_MISC_CTL2.read_data_byte_number =
- * = NFI_CON.sector_number * NFI_SECCUS.custom_sector_size
- */
-- val = as_ctrl->nfi_cfg.sec_size * as_ctrl->nfi_cfg.sec_num;
-- val = FIELD_PREP(SPI_NFI_READ_DATA_BYTE_NUM, val);
- err = regmap_update_bits(as_ctrl->regmap_nfi,
- REG_SPI_NFI_SNF_MISC_CTL2,
-- SPI_NFI_READ_DATA_BYTE_NUM, val);
-+ SPI_NFI_READ_DATA_BYTE_NUM,
-+ FIELD_PREP(SPI_NFI_READ_DATA_BYTE_NUM, bytes));
- if (err)
- goto error_dma_unmap;
-
-@@ -826,10 +828,13 @@ static ssize_t airoha_snand_dirmap_write
- struct airoha_snand_ctrl *as_ctrl;
- dma_addr_t dma_addr;
- u32 wr_mode, val, opcode;
-+ size_t bytes;
- int err;
-
- as_ctrl = spi_controller_get_devdata(spi->controller);
-
-+ bytes = as_ctrl->nfi_cfg.sec_num * as_ctrl->nfi_cfg.sec_size;
-+
- opcode = desc->info.op_tmpl.cmd.opcode;
- switch (opcode) {
- case SPI_NAND_OP_PROGRAM_LOAD_SINGLE:
-@@ -884,18 +889,17 @@ static ssize_t airoha_snand_dirmap_write
- goto error_dma_mode_off;
-
- /* Set number of sector will be written */
-- val = FIELD_PREP(SPI_NFI_SEC_NUM, as_ctrl->nfi_cfg.sec_num);
- err = regmap_update_bits(as_ctrl->regmap_nfi, REG_SPI_NFI_CON,
-- SPI_NFI_SEC_NUM, val);
-+ SPI_NFI_SEC_NUM,
-+ FIELD_PREP(SPI_NFI_SEC_NUM, 1));
- if (err)
- goto error_dma_mode_off;
-
- /* Set custom sector size */
-- val = as_ctrl->nfi_cfg.sec_size;
- err = regmap_update_bits(as_ctrl->regmap_nfi, REG_SPI_NFI_SECCUS_SIZE,
- SPI_NFI_CUS_SEC_SIZE |
- SPI_NFI_CUS_SEC_SIZE_EN,
-- FIELD_PREP(SPI_NFI_CUS_SEC_SIZE, val) |
-+ FIELD_PREP(SPI_NFI_CUS_SEC_SIZE, bytes) |
- SPI_NFI_CUS_SEC_SIZE_EN);
- if (err)
- goto error_dma_mode_off;
-@@ -920,11 +924,10 @@ static ssize_t airoha_snand_dirmap_write
- * = NFI_SNF_MISC_CTL2.write_data_byte_number =
- * = NFI_CON.sector_number * NFI_SECCUS.custom_sector_size
- */
-- val = as_ctrl->nfi_cfg.sec_size * as_ctrl->nfi_cfg.sec_num;
-- val = FIELD_PREP(SPI_NFI_PROG_LOAD_BYTE_NUM, val);
- err = regmap_update_bits(as_ctrl->regmap_nfi,
- REG_SPI_NFI_SNF_MISC_CTL2,
-- SPI_NFI_PROG_LOAD_BYTE_NUM, val);
-+ SPI_NFI_PROG_LOAD_BYTE_NUM,
-+ FIELD_PREP(SPI_NFI_PROG_LOAD_BYTE_NUM, bytes));
- if (err)
- goto error_dma_unmap;
-
--- /dev/null
+From 902c0ea18a97b1a6eeee5799cb1fd9a79ef9208e Mon Sep 17 00:00:00 2001
+From: Mikhail Kshevetskiy <mikhail.kshevetskiy@iopsys.eu>
+Date: Sun, 12 Oct 2025 15:17:03 +0300
+Subject: [PATCH 12/14] spi: airoha: avoid reading flash page settings from
+ SNFI registers during driver startup
+
+The spinand driver do 3 type of dirmap requests:
+ * read/write whole flash page without oob
+ (offs = 0, len = page_size)
+ * read/write whole flash page including oob
+ (offs = 0, len = page_size + oob_size)
+ * read/write oob area only
+ (offs = page_size, len = oob_size)
+
+The trick is:
+ * read/write a single "sector"
+ * set a custom sector size equal to offs + len. It's a bit safer to
+ rounded up "sector size" value 64.
+ * set the transfer length equal to custom sector size
+
+And it works!
+
+Thus we can remove a dirty hack that reads flash page settings from
+SNFI registers during driver startup. Also airoha_snand_adjust_op_size()
+function becomes unnecessary.
+
+Signed-off-by: Mikhail Kshevetskiy <mikhail.kshevetskiy@iopsys.eu>
+Link: https://patch.msgid.link/20251012121707.2296160-13-mikhail.kshevetskiy@iopsys.eu
+Signed-off-by: Mark Brown <broonie@kernel.org>
+---
+ drivers/spi/spi-airoha-snfi.c | 115 ++--------------------------------
+ 1 file changed, 5 insertions(+), 110 deletions(-)
+
+--- a/drivers/spi/spi-airoha-snfi.c
++++ b/drivers/spi/spi-airoha-snfi.c
+@@ -223,13 +223,6 @@ struct airoha_snand_ctrl {
+ struct regmap *regmap_ctrl;
+ struct regmap *regmap_nfi;
+ struct clk *spi_clk;
+-
+- struct {
+- size_t page_size;
+- size_t sec_size;
+- u8 sec_num;
+- u8 spare_size;
+- } nfi_cfg;
+ };
+
+ static int airoha_snand_set_fifo_op(struct airoha_snand_ctrl *as_ctrl,
+@@ -490,55 +483,6 @@ static int airoha_snand_nfi_init(struct
+ SPI_NFI_ALL_IRQ_EN, SPI_NFI_AHB_DONE_EN);
+ }
+
+-static int airoha_snand_nfi_config(struct airoha_snand_ctrl *as_ctrl)
+-{
+- int err;
+- u32 val;
+-
+- err = regmap_write(as_ctrl->regmap_nfi, REG_SPI_NFI_CON,
+- SPI_NFI_FIFO_FLUSH | SPI_NFI_RST);
+- if (err)
+- return err;
+-
+- /* auto FDM */
+- err = regmap_clear_bits(as_ctrl->regmap_nfi, REG_SPI_NFI_CNFG,
+- SPI_NFI_AUTO_FDM_EN);
+- if (err)
+- return err;
+-
+- /* HW ECC */
+- err = regmap_clear_bits(as_ctrl->regmap_nfi, REG_SPI_NFI_CNFG,
+- SPI_NFI_HW_ECC_EN);
+- if (err)
+- return err;
+-
+- /* DMA Burst */
+- err = regmap_set_bits(as_ctrl->regmap_nfi, REG_SPI_NFI_CNFG,
+- SPI_NFI_DMA_BURST_EN);
+- if (err)
+- return err;
+-
+- /* sec num */
+- val = FIELD_PREP(SPI_NFI_SEC_NUM, 1);
+- err = regmap_update_bits(as_ctrl->regmap_nfi, REG_SPI_NFI_CON,
+- SPI_NFI_SEC_NUM, val);
+- if (err)
+- return err;
+-
+- /* enable cust sec size */
+- err = regmap_set_bits(as_ctrl->regmap_nfi, REG_SPI_NFI_SECCUS_SIZE,
+- SPI_NFI_CUS_SEC_SIZE_EN);
+- if (err)
+- return err;
+-
+- /* set cust sec size */
+- val = FIELD_PREP(SPI_NFI_CUS_SEC_SIZE,
+- as_ctrl->nfi_cfg.sec_size * as_ctrl->nfi_cfg.sec_num);
+- return regmap_update_bits(as_ctrl->regmap_nfi,
+- REG_SPI_NFI_SECCUS_SIZE,
+- SPI_NFI_CUS_SEC_SIZE, val);
+-}
+-
+ static bool airoha_snand_is_page_ops(const struct spi_mem_op *op)
+ {
+ if (op->addr.nbytes != 2)
+@@ -571,26 +515,6 @@ static bool airoha_snand_is_page_ops(con
+ }
+ }
+
+-static int airoha_snand_adjust_op_size(struct spi_mem *mem,
+- struct spi_mem_op *op)
+-{
+- size_t max_len;
+-
+- if (airoha_snand_is_page_ops(op)) {
+- struct airoha_snand_ctrl *as_ctrl;
+-
+- as_ctrl = spi_controller_get_devdata(mem->spi->controller);
+- max_len = as_ctrl->nfi_cfg.sec_size;
+- max_len += as_ctrl->nfi_cfg.spare_size;
+- max_len *= as_ctrl->nfi_cfg.sec_num;
+-
+- if (op->data.nbytes > max_len)
+- op->data.nbytes = max_len;
+- }
+-
+- return 0;
+-}
+-
+ static bool airoha_snand_supports_op(struct spi_mem *mem,
+ const struct spi_mem_op *op)
+ {
+@@ -641,7 +565,8 @@ static ssize_t airoha_snand_dirmap_read(
+
+ as_ctrl = spi_controller_get_devdata(spi->controller);
+
+- bytes = as_ctrl->nfi_cfg.sec_num * as_ctrl->nfi_cfg.sec_size;
++ /* minimum oob size is 64 */
++ bytes = round_up(offs + len, 64);
+
+ /*
+ * DUALIO and QUADIO opcodes are not supported by the spi controller,
+@@ -833,7 +758,8 @@ static ssize_t airoha_snand_dirmap_write
+
+ as_ctrl = spi_controller_get_devdata(spi->controller);
+
+- bytes = as_ctrl->nfi_cfg.sec_num * as_ctrl->nfi_cfg.sec_size;
++ /* minimum oob size is 64 */
++ bytes = round_up(offs + len, 64);
+
+ opcode = desc->info.op_tmpl.cmd.opcode;
+ switch (opcode) {
+@@ -1076,7 +1002,6 @@ static int airoha_snand_exec_op(struct s
+ }
+
+ static const struct spi_controller_mem_ops airoha_snand_mem_ops = {
+- .adjust_op_size = airoha_snand_adjust_op_size,
+ .supports_op = airoha_snand_supports_op,
+ .exec_op = airoha_snand_exec_op,
+ .dirmap_create = airoha_snand_dirmap_create,
+@@ -1101,36 +1026,6 @@ static int airoha_snand_setup(struct spi
+ return 0;
+ }
+
+-static int airoha_snand_nfi_setup(struct airoha_snand_ctrl *as_ctrl)
+-{
+- u32 val, sec_size, sec_num;
+- int err;
+-
+- err = regmap_read(as_ctrl->regmap_nfi, REG_SPI_NFI_CON, &val);
+- if (err)
+- return err;
+-
+- sec_num = FIELD_GET(SPI_NFI_SEC_NUM, val);
+-
+- err = regmap_read(as_ctrl->regmap_nfi, REG_SPI_NFI_SECCUS_SIZE, &val);
+- if (err)
+- return err;
+-
+- sec_size = FIELD_GET(SPI_NFI_CUS_SEC_SIZE, val);
+-
+- /* init default value */
+- as_ctrl->nfi_cfg.sec_size = sec_size;
+- as_ctrl->nfi_cfg.sec_num = sec_num;
+- as_ctrl->nfi_cfg.page_size = round_down(sec_size * sec_num, 1024);
+- as_ctrl->nfi_cfg.spare_size = 16;
+-
+- err = airoha_snand_nfi_init(as_ctrl);
+- if (err)
+- return err;
+-
+- return airoha_snand_nfi_config(as_ctrl);
+-}
+-
+ static const struct regmap_config spi_ctrl_regmap_config = {
+ .name = "ctrl",
+ .reg_bits = 32,
+@@ -1204,7 +1099,7 @@ static int airoha_snand_probe(struct pla
+ ctrl->setup = airoha_snand_setup;
+ device_set_node(&ctrl->dev, dev_fwnode(dev));
+
+- err = airoha_snand_nfi_setup(as_ctrl);
++ err = airoha_snand_nfi_init(as_ctrl);
+ if (err)
+ return err;
+
+++ /dev/null
-From 64a4d6e84145227211485067022cd4e5cf052e04 Mon Sep 17 00:00:00 2001
-From: Mikhail Kshevetskiy <mikhail.kshevetskiy@iopsys.eu>
-Date: Thu, 14 Aug 2025 23:56:24 +0300
-Subject: [PATCH v6 13/13] spi: airoha: avoid reading flash page settings from
- SNFI registers during driver startup
-
-The spinand driver do 3 type of dirmap requests:
- * read/write whole flash page without oob
- (offs = 0, len = page_size)
- * read/write whole flash page including oob
- (offs = 0, len = page_size + oob_size)
- * read/write oob area only
- (offs = page_size, len = oob_size)
-
-The trick is:
- * read/write a single "sector"
- * set a custom sector size equal to offs + len. It's a bit safer to
- rounded up "sector size" value 64.
- * set the transfer length equal to custom sector size
-
-And it works!
-
-Thus we can remove a dirty hack that reads flash page settings from
-SNFI registers during driver startup. Also airoha_snand_adjust_op_size()
-function becomes unnecessary.
-
-Signed-off-by: Mikhail Kshevetskiy <mikhail.kshevetskiy@iopsys.eu>
----
- drivers/spi/spi-airoha-snfi.c | 115 ++--------------------------------
- 1 file changed, 5 insertions(+), 110 deletions(-)
-
---- a/drivers/spi/spi-airoha-snfi.c
-+++ b/drivers/spi/spi-airoha-snfi.c
-@@ -223,13 +223,6 @@ struct airoha_snand_ctrl {
- struct regmap *regmap_ctrl;
- struct regmap *regmap_nfi;
- struct clk *spi_clk;
--
-- struct {
-- size_t page_size;
-- size_t sec_size;
-- u8 sec_num;
-- u8 spare_size;
-- } nfi_cfg;
- };
-
- static int airoha_snand_set_fifo_op(struct airoha_snand_ctrl *as_ctrl,
-@@ -490,55 +483,6 @@ static int airoha_snand_nfi_init(struct
- SPI_NFI_ALL_IRQ_EN, SPI_NFI_AHB_DONE_EN);
- }
-
--static int airoha_snand_nfi_config(struct airoha_snand_ctrl *as_ctrl)
--{
-- int err;
-- u32 val;
--
-- err = regmap_write(as_ctrl->regmap_nfi, REG_SPI_NFI_CON,
-- SPI_NFI_FIFO_FLUSH | SPI_NFI_RST);
-- if (err)
-- return err;
--
-- /* auto FDM */
-- err = regmap_clear_bits(as_ctrl->regmap_nfi, REG_SPI_NFI_CNFG,
-- SPI_NFI_AUTO_FDM_EN);
-- if (err)
-- return err;
--
-- /* HW ECC */
-- err = regmap_clear_bits(as_ctrl->regmap_nfi, REG_SPI_NFI_CNFG,
-- SPI_NFI_HW_ECC_EN);
-- if (err)
-- return err;
--
-- /* DMA Burst */
-- err = regmap_set_bits(as_ctrl->regmap_nfi, REG_SPI_NFI_CNFG,
-- SPI_NFI_DMA_BURST_EN);
-- if (err)
-- return err;
--
-- /* sec num */
-- val = FIELD_PREP(SPI_NFI_SEC_NUM, 1);
-- err = regmap_update_bits(as_ctrl->regmap_nfi, REG_SPI_NFI_CON,
-- SPI_NFI_SEC_NUM, val);
-- if (err)
-- return err;
--
-- /* enable cust sec size */
-- err = regmap_set_bits(as_ctrl->regmap_nfi, REG_SPI_NFI_SECCUS_SIZE,
-- SPI_NFI_CUS_SEC_SIZE_EN);
-- if (err)
-- return err;
--
-- /* set cust sec size */
-- val = FIELD_PREP(SPI_NFI_CUS_SEC_SIZE,
-- as_ctrl->nfi_cfg.sec_size * as_ctrl->nfi_cfg.sec_num);
-- return regmap_update_bits(as_ctrl->regmap_nfi,
-- REG_SPI_NFI_SECCUS_SIZE,
-- SPI_NFI_CUS_SEC_SIZE, val);
--}
--
- static bool airoha_snand_is_page_ops(const struct spi_mem_op *op)
- {
- if (op->addr.nbytes != 2)
-@@ -571,26 +515,6 @@ static bool airoha_snand_is_page_ops(con
- }
- }
-
--static int airoha_snand_adjust_op_size(struct spi_mem *mem,
-- struct spi_mem_op *op)
--{
-- size_t max_len;
--
-- if (airoha_snand_is_page_ops(op)) {
-- struct airoha_snand_ctrl *as_ctrl;
--
-- as_ctrl = spi_controller_get_devdata(mem->spi->controller);
-- max_len = as_ctrl->nfi_cfg.sec_size;
-- max_len += as_ctrl->nfi_cfg.spare_size;
-- max_len *= as_ctrl->nfi_cfg.sec_num;
--
-- if (op->data.nbytes > max_len)
-- op->data.nbytes = max_len;
-- }
--
-- return 0;
--}
--
- static bool airoha_snand_supports_op(struct spi_mem *mem,
- const struct spi_mem_op *op)
- {
-@@ -641,7 +565,8 @@ static ssize_t airoha_snand_dirmap_read(
-
- as_ctrl = spi_controller_get_devdata(spi->controller);
-
-- bytes = as_ctrl->nfi_cfg.sec_num * as_ctrl->nfi_cfg.sec_size;
-+ /* minimum oob size is 64 */
-+ bytes = round_up(offs + len, 64);
-
- /*
- * DUALIO and QUADIO opcodes are not supported by the spi controller,
-@@ -833,7 +758,8 @@ static ssize_t airoha_snand_dirmap_write
-
- as_ctrl = spi_controller_get_devdata(spi->controller);
-
-- bytes = as_ctrl->nfi_cfg.sec_num * as_ctrl->nfi_cfg.sec_size;
-+ /* minimum oob size is 64 */
-+ bytes = round_up(offs + len, 64);
-
- opcode = desc->info.op_tmpl.cmd.opcode;
- switch (opcode) {
-@@ -1080,7 +1006,6 @@ static int airoha_snand_exec_op(struct s
- }
-
- static const struct spi_controller_mem_ops airoha_snand_mem_ops = {
-- .adjust_op_size = airoha_snand_adjust_op_size,
- .supports_op = airoha_snand_supports_op,
- .exec_op = airoha_snand_exec_op,
- .dirmap_create = airoha_snand_dirmap_create,
-@@ -1105,36 +1030,6 @@ static int airoha_snand_setup(struct spi
- return 0;
- }
-
--static int airoha_snand_nfi_setup(struct airoha_snand_ctrl *as_ctrl)
--{
-- u32 val, sec_size, sec_num;
-- int err;
--
-- err = regmap_read(as_ctrl->regmap_nfi, REG_SPI_NFI_CON, &val);
-- if (err)
-- return err;
--
-- sec_num = FIELD_GET(SPI_NFI_SEC_NUM, val);
--
-- err = regmap_read(as_ctrl->regmap_nfi, REG_SPI_NFI_SECCUS_SIZE, &val);
-- if (err)
-- return err;
--
-- sec_size = FIELD_GET(SPI_NFI_CUS_SEC_SIZE, val);
--
-- /* init default value */
-- as_ctrl->nfi_cfg.sec_size = sec_size;
-- as_ctrl->nfi_cfg.sec_num = sec_num;
-- as_ctrl->nfi_cfg.page_size = round_down(sec_size * sec_num, 1024);
-- as_ctrl->nfi_cfg.spare_size = 16;
--
-- err = airoha_snand_nfi_init(as_ctrl);
-- if (err)
-- return err;
--
-- return airoha_snand_nfi_config(as_ctrl);
--}
--
- static const struct regmap_config spi_ctrl_regmap_config = {
- .name = "ctrl",
- .reg_bits = 32,
-@@ -1208,7 +1103,7 @@ static int airoha_snand_probe(struct pla
- ctrl->setup = airoha_snand_setup;
- device_set_node(&ctrl->dev, dev_fwnode(dev));
-
-- err = airoha_snand_nfi_setup(as_ctrl);
-+ err = airoha_snand_nfi_init(as_ctrl);
- if (err)
- return err;
-
--- /dev/null
+From 0743acf746a81e0460a56fd5ff847d97fa7eb370 Mon Sep 17 00:00:00 2001
+From: Mikhail Kshevetskiy <mikhail.kshevetskiy@iopsys.eu>
+Date: Sun, 12 Oct 2025 15:17:04 +0300
+Subject: [PATCH 13/14] spi: airoha: buffer must be 0xff-ed before writing
+
+During writing, the entire flash page (including OOB) will be updated
+with the values from the temporary buffer, so we need to fill the
+untouched areas of the buffer with 0xff value to prevent accidental
+data overwriting.
+
+Signed-off-by: Mikhail Kshevetskiy <mikhail.kshevetskiy@iopsys.eu>
+Reviewed-by: AngeloGioacchino Del Regno <angelogioacchino.delregno@collabora.com>
+Link: https://patch.msgid.link/20251012121707.2296160-14-mikhail.kshevetskiy@iopsys.eu
+Signed-off-by: Mark Brown <broonie@kernel.org>
+---
+ drivers/spi/spi-airoha-snfi.c | 4 ++++
+ 1 file changed, 4 insertions(+)
+
+--- a/drivers/spi/spi-airoha-snfi.c
++++ b/drivers/spi/spi-airoha-snfi.c
+@@ -776,7 +776,11 @@ static ssize_t airoha_snand_dirmap_write
+ return -EOPNOTSUPP;
+ }
+
++ if (offs > 0)
++ memset(txrx_buf, 0xff, offs);
+ memcpy(txrx_buf + offs, buf, len);
++ if (bytes > offs + len)
++ memset(txrx_buf + offs + len, 0xff, bytes - offs - len);
+
+ err = airoha_snand_set_mode(as_ctrl, SPI_MODE_DMA);
+ if (err < 0)
+++ /dev/null
-From 86f88e604305186aec1fb6eebbf8f0a42c2435d3 Mon Sep 17 00:00:00 2001
-From: Mikhail Kshevetskiy <mikhail.kshevetskiy@iopsys.eu>
-Date: Thu, 9 Oct 2025 19:33:23 +0300
-Subject: [PATCH] spi: airoha-snfi: en7523: workaround flash damaging
- if UART_TXD was short to GND
-
-We found that some serial console may pull TX line to GROUND during board
-boot time. Airoha uses TX line as one of it's BOOT pins. This will lead
-to booting in RESERVED boot mode.
-
-It was found that some flashes operates incorrectly in RESERVED mode.
-Micron and Skyhigh flashes are definitely affected by the issue,
-Winbond flashes are NOT affected.
-
-Details:
---------
-DMA reading of odd pages on affected flashes operates incorrectly. Page
-reading offset (start of the page) on hardware level is replaced by 0x10.
-Thus results in incorrect data reading. Usage of UBI make things even
-worse. Any attempt to access UBI leads to ubi damaging. As result OS loading
-becomes impossible.
-
-Non-DMA reading is OK.
-
-This patch detects booting in reserved mode, turn off DMA and print big
-fat warning.
-
-Signed-off-by: Mikhail Kshevetskiy <mikhail.kshevetskiy@iopsys.eu>
----
- drivers/spi/spi-airoha-snfi.c | 40 ++++++++++++++++++++++++++++++-----
- 1 file changed, 35 insertions(+), 5 deletions(-)
-
---- a/drivers/spi/spi-airoha-snfi.c
-+++ b/drivers/spi/spi-airoha-snfi.c
-@@ -1013,6 +1013,11 @@ static const struct spi_controller_mem_o
- .dirmap_write = airoha_snand_dirmap_write,
- };
-
-+static const struct spi_controller_mem_ops airoha_snand_nodma_mem_ops = {
-+ .supports_op = airoha_snand_supports_op,
-+ .exec_op = airoha_snand_exec_op,
-+};
-+
- static int airoha_snand_setup(struct spi_device *spi)
- {
- struct airoha_snand_ctrl *as_ctrl;
-@@ -1058,7 +1063,8 @@ static int airoha_snand_probe(struct pla
- struct device *dev = &pdev->dev;
- struct spi_controller *ctrl;
- void __iomem *base;
-- int err;
-+ int err, dma_enabled;
-+ u32 sfc_strap;
-
- ctrl = devm_spi_alloc_host(dev, sizeof(*as_ctrl));
- if (!ctrl)
-@@ -1092,12 +1098,36 @@ static int airoha_snand_probe(struct pla
- return dev_err_probe(dev, PTR_ERR(as_ctrl->spi_clk),
- "unable to get spi clk\n");
-
-- err = dma_set_mask(as_ctrl->dev, DMA_BIT_MASK(32));
-- if (err)
-- return err;
-+ dma_enabled = 1;
-+ if (device_is_compatible(dev, "airoha,en7523-snand")) {
-+ err = regmap_read(as_ctrl->regmap_ctrl,
-+ REG_SPI_CTRL_SFC_STRAP, &sfc_strap);
-+ if (err)
-+ return err;
-+
-+ if (!(sfc_strap & 0x04)) {
-+ dma_enabled = 0;
-+ printk(KERN_WARNING "\n"
-+ "=== WARNING ======================================================\n"
-+ "Detected booting in RESERVED mode (UART_TXD was short to GND).\n"
-+ "This mode is known for incorrect DMA reading of some flashes.\n"
-+ "Usage of DMA for flash operations will be disabled to prevent data\n"
-+ "damage. Unplug your serial console and power cycle the board\n"
-+ "to boot with full performance.\n"
-+ "==================================================================\n\n");
-+ }
-+ }
-+
-+ if (dma_enabled) {
-+ err = dma_set_mask(as_ctrl->dev, DMA_BIT_MASK(32));
-+ if (err)
-+ return err;
-+ }
-
- ctrl->num_chipselect = 2;
-- ctrl->mem_ops = &airoha_snand_mem_ops;
-+ ctrl->mem_ops = dma_enabled ?
-+ &airoha_snand_mem_ops :
-+ &airoha_snand_nodma_mem_ops;
- ctrl->bits_per_word_mask = SPI_BPW_MASK(8);
- ctrl->mode_bits = SPI_RX_DUAL;
- ctrl->setup = airoha_snand_setup;
--- /dev/null
+From 061795b345aff371df8f71d54ae7c7dc8ae630d0 Mon Sep 17 00:00:00 2001
+From: Mikhail Kshevetskiy <mikhail.kshevetskiy@iopsys.eu>
+Date: Wed, 26 Nov 2025 02:40:45 +0300
+Subject: [PATCH 14/14] spi: airoha-snfi: en7523: workaround flash damaging if
+ UART_TXD was short to GND
+
+Airoha EN7523 specific bug
+--------------------------
+We found that some serial console may pull TX line to GROUND during board
+boot time. Airoha uses TX line as one of its bootstrap pins. On the EN7523
+SoC this may lead to booting in RESERVED boot mode.
+
+It was found that some flashes operates incorrectly in RESERVED mode.
+Micron and Skyhigh flashes are definitely affected by the issue,
+Winbond flashes are not affected.
+
+Details:
+--------
+DMA reading of odd pages on affected flashes operates incorrectly. Page
+reading offset (start of the page) on hardware level is replaced by 0x10.
+Thus results in incorrect data reading. As result OS loading becomes
+impossible.
+
+Usage of UBI make things even worse. On attaching, UBI will detects
+corruptions (because of wrong reading of odd pages) and will try to
+recover. For recovering UBI will erase and write 'damaged' blocks with
+a valid information. This will destroy all UBI data.
+
+Non-DMA reading is OK.
+
+This patch detects booting in reserved mode, turn off DMA and print big
+fat warning.
+
+It's worth noting that the boot configuration is preserved across reboots.
+Therefore, to boot normally, you should do the following:
+- disconnect the serial console from the board,
+- power cycle the board.
+
+Fixes: a403997c12019 ("spi: airoha: add SPI-NAND Flash controller driver")
+Signed-off-by: Mikhail Kshevetskiy <mikhail.kshevetskiy@iopsys.eu>
+Reviewed-by: AngeloGioacchino Del Regno <angelogioacchino.delregno@collabora.com>
+Reviewed-by: Andy Shevchenko <andriy.shevchenko@intel.com>
+Link: https://patch.msgid.link/20251125234047.1101985-2-mikhail.kshevetskiy@iopsys.eu
+Signed-off-by: Mark Brown <broonie@kernel.org>
+---
+ drivers/spi/spi-airoha-snfi.c | 25 ++++++++++++++++++++++++-
+ 1 file changed, 24 insertions(+), 1 deletion(-)
+
+--- a/drivers/spi/spi-airoha-snfi.c
++++ b/drivers/spi/spi-airoha-snfi.c
+@@ -1013,6 +1013,11 @@ static const struct spi_controller_mem_o
+ .dirmap_write = airoha_snand_dirmap_write,
+ };
+
++static const struct spi_controller_mem_ops airoha_snand_nodma_mem_ops = {
++ .supports_op = airoha_snand_supports_op,
++ .exec_op = airoha_snand_exec_op,
++};
++
+ static int airoha_snand_setup(struct spi_device *spi)
+ {
+ struct airoha_snand_ctrl *as_ctrl;
+@@ -1057,7 +1062,9 @@ static int airoha_snand_probe(struct pla
+ struct airoha_snand_ctrl *as_ctrl;
+ struct device *dev = &pdev->dev;
+ struct spi_controller *ctrl;
++ bool dma_enable = true;
+ void __iomem *base;
++ u32 sfc_strap;
+ int err;
+
+ ctrl = devm_spi_alloc_host(dev, sizeof(*as_ctrl));
+@@ -1092,12 +1099,28 @@ static int airoha_snand_probe(struct pla
+ return dev_err_probe(dev, PTR_ERR(as_ctrl->spi_clk),
+ "unable to get spi clk\n");
+
++ if (device_is_compatible(dev, "airoha,en7523-snand")) {
++ err = regmap_read(as_ctrl->regmap_ctrl,
++ REG_SPI_CTRL_SFC_STRAP, &sfc_strap);
++ if (err)
++ return err;
++
++ if (!(sfc_strap & 0x04)) {
++ dma_enable = false;
++ dev_warn(dev, "Detected booting in RESERVED mode (UART_TXD was short to GND).\n");
++ dev_warn(dev, "This mode is known for incorrect DMA reading of some flashes.\n");
++ dev_warn(dev, "Much slower PIO mode will be used to prevent flash data damage.\n");
++ dev_warn(dev, "Unplug UART cable and power cycle board to get full performance.\n");
++ }
++ }
++
+ err = dma_set_mask(as_ctrl->dev, DMA_BIT_MASK(32));
+ if (err)
+ return err;
+
+ ctrl->num_chipselect = 2;
+- ctrl->mem_ops = &airoha_snand_mem_ops;
++ ctrl->mem_ops = dma_enable ? &airoha_snand_mem_ops
++ : &airoha_snand_nodma_mem_ops;
+ ctrl->bits_per_word_mask = SPI_BPW_MASK(8);
+ ctrl->mode_bits = SPI_RX_DUAL;
+ ctrl->setup = airoha_snand_setup;
--- /dev/null
+From bb2f9b3d71717c7df942deb1488c56e544d4a32c Mon Sep 17 00:00:00 2001
+From: Mikhail Kshevetskiy <mikhail.kshevetskiy@iopsys.eu>
+Date: Fri, 10 Oct 2025 06:01:33 +0300
+Subject: [PATCH v6 3/3] arm: dts: airoha: en7523: add SNAND node
+
+Add SNAND node to enable support of attached SPI-NAND on the EN7523 SoC.
+
+Signed-off-by: Mikhail Kshevetskiy <mikhail.kshevetskiy@iopsys.eu>
+Reviewed-by: AngeloGioacchino Del Regno <angelogioacchino.delregno@collabora.com>
+---
+ arch/arm/boot/dts/airoha/en7523.dtsi | 20 ++++++++++++++++++++
+ 1 file changed, 20 insertions(+)
+
+--- a/arch/arm/boot/dts/airoha/en7523.dtsi
++++ b/arch/arm/boot/dts/airoha/en7523.dtsi
+@@ -203,4 +203,24 @@
+ #interrupt-cells = <1>;
+ };
+ };
++
++ spi_ctrl: spi@1fa10000 {
++ compatible = "airoha,en7523-snand", "airoha,en7581-snand";
++ reg = <0x1fa10000 0x140>,
++ <0x1fa11000 0x160>;
++
++ clocks = <&scu EN7523_CLK_SPI>;
++ clock-names = "spi";
++
++ #address-cells = <1>;
++ #size-cells = <0>;
++
++ nand: nand@0 {
++ compatible = "spi-nand";
++ reg = <0>;
++ spi-max-frequency = <50000000>;
++ spi-tx-bus-width = <1>;
++ spi-rx-bus-width = <2>;
++ };
++ };
+ };